package com.sparrow.common.ratelimit;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 把请求看作“水”，水流进一个固定容量的桶。
 * 桶里的水以固定速率“漏出”，多余的水（超过桶容量）会被丢弃。
 * 不管请求进来的速度快慢，出桶速率固定，保证平滑流量。
 *
 *
 * 漏桶限流器：固定速率处理请求，支持阻塞等待（acquire）。
 * 一般要结合队列使用，比如请求到来的时候如果桶没有满就允许(将请求放入队列供消费线程消费)，否则拒绝请求
 * 固定速率流出请求，
 * 如果加水acquire()超过固定速率流出，则代表桶满了，需要等待或者抛弃
 * 打个比喻：厕所排队，一个人出来一个人才进去。你来了发现人太多了，只能等（或者不进了）
 */
public class LeakyBucket {
    private final int capacity;              // 桶容量
    private final double leakRate;           // 每秒处理速率
    private double water;                    // 当前桶内水量
    private final ScheduledExecutorService scheduler;

    public LeakyBucket(int capacity, double leakRate) {
        this.capacity = capacity;
        this.leakRate = leakRate;
        this.water = 0.0;

        scheduler = Executors.newSingleThreadScheduledExecutor();
        scheduler.scheduleAtFixedRate(this::leak, 100, 100, TimeUnit.MILLISECONDS);
    }

    /**
     * 加水操作
     * 非阻塞尝试：桶未满则允许请求，否则返回 false。
     */
    public synchronized boolean tryAcquire() {
        if (water < capacity) {
            water++;
            return true;
        } else {
            return false;
        }
    }

    /**
     * 加水操作
     * 阻塞获取：桶满时等待直到有足够空间。
     */
    public void acquire() throws InterruptedException {
        synchronized (this) {
            while (water >= capacity) {
                this.wait();
            }
            water++;
        }
    }

    /**
     * 漏水操作：每 100ms 漏出 leakRate/10 个请求。
     */
    private synchronized void leak() {
        double leaked = leakRate / 10.0;
        if (water > 0) {
            double oldWater = water;
            water = Math.max(0, water - leaked);
            if ((int) oldWater > (int) water) {
                this.notifyAll(); // 唤醒等待线程，仅当整数变化时
            }
        }
    }

    public void shutdown() {
        scheduler.shutdownNow();
    }
}


